package twelfth;

// Java IO
public class Note {
}
/**
 * 在java.io包中最重要的就是5个类 1个接口，5个类指的是File OutputStream InputStream Writer Reader。1个接口就是Serializable
 * 掌握这些就掌握了IO操作的核心
 * <p>
 * 操作文件的类---File
 * 在整个IO包中，唯一表示与文件本身有关的类就是File类。使用File类可以进行创建或删除文件等常用操作。
 * 想要使用File类 就要观察File类的构造方法，此类的构造方法如下：
 * File类的构造方法：
 * public File(String pathname) --> 实例化FIle类的时候必须设置好文件路径。
 * 比如现在想操作D盘下面的 test.txt文件 则路径就必须写成“ d:\\text.txt"  \\表示一个 \
 * 而要操作文件还需要File类中定义的若干方法：
 * File类中的主要方法和常量：
 * 常量：
 * public static final String pathSeparator 表示路径的分隔符（windows是：“;"
 * public static final String separator 表示路径的分隔符是（windows是：" \ "
 * 方法：
 * File(String pathname)：创建File类对象，传入完整路径
 * File(File parent,String child)：根据指定的父类静创建子文件
 * boolean createNewFile()：创建新文件
 * boolean delete()：删除文件
 * booolean exists()：判断文件是否存在
 * boolean isDirectory()：判断给定的路径是都是一个目录
 * long lenth()：返回文件的大小
 * String[] list()：列出指定目录的全部内容，只是列出了名称
 * File[] listFiles()：列出指定目录的全部内容，会列出路径
 * boolean mkdir()：创建一个目录
 * boolean mkdirs()：创建多级目录
 * boolean renameTo(File dest)：为已有的文件重新命名
 * long lastModified()：取得文件的最后一次修改日期时间
 * File getParentFile()：取得当前路径的父路径
 * <p>
 * 提示：为什么File类中的常量定义的命名规则不符合标准命名规则
 * 之前讲Java命名规则的时候说一个常量的全部标识符应该全部大写，但是定义pathSeparator和separator时候没有按照标准执行
 * 答：
 * 不符合Java的标准命名股则是因为Java的发展经过了一段相当长的时间，而命名规范是逐步形成的，
 * File类因为出现比较早，所以当时并没有对命名规范也有严格的要求，这些都哦属于Java的历史遗留问题。
 * <p>
 * RandomAccessFile类
 * File类只是针对文件本身进行操作，而如果想对内容进行操作，可以用RandomAccessFile类，此属性属于随机读取类，
 * 可以随机得到读取一个文件中指定位置的数据。
 * 现在假设在文件中保存一下3个数据：
 * ①zhangsan ，30
 * ②lis ，31
 * ③wangwu ， 32
 * 那么如果使用RandomAccessFile类读取”lisi“信息时，就可以将”zhangsan“的信息跳过，
 * 相当于在文件中设置了一个指针，根据此指针的位置进行读取。
 * 但是如果想实现这样的功能，则每个数据的长度应该保持一致，所以在设置姓名的时候应该保持一致，
 * 所以在色湖之姓名的时候统一为8位长度，数字为4位长度。
 * 8位字符串            4位
 * z h a n g s a n         30      zhangsan的信息，1-12位保存
 * l s                     21      lis的信息：13-24位保存
 * w a n g w u             32      wangwu的信息：25-32位保存
 * 要实现以上的功能 必须依靠RandomAccess中的设置模式，在构造方法中传递此模式，此类的常用操作方法有：
 * RandomAccessFile(File file,String mode)：接收File类的对象，指定操作路径，但是在设置时需要设置模式，”r"为只读,”w"为只写，“rw”为读写
 * RandomAccessFile(String name,String mode)：不再使用File类对象表示文件，而是直接输入了一个固定的文件路径
 * close()：关闭操作
 * read(byte[] b)：将内容读取到一个byte数组之中
 * readByte()：读取一个字节
 * readInt()：从文件中读取整型数据
 * seek(long pos)：设置读指针的位置
 * writeBytes(String s)：将一个字符串写入到文件中，按照字节的方式处理
 * writeInt(int v)：将一个int类型数据写入文件，长度为4位
 * skipBytes(int n)：指针跳过多少字节
 * 需要注意：如果使用rw的方式声明RandomAccessFile对象时，要写入的文件不存在，系统将自动进行创建
 * <p>
 * 字节流与字符流基本操作
 * 在程序中所有的数据都是以流的方式进行传输或者保存的，程序需要数据的时候要使用输入流读取数据，
 * 而当程序需要将一些数据保存起来的时候，就要使用输出流完成。
 * （我觉得 输入输出相对于的对象是”程序“，如果需要数据就输入流，保存文件就输出流。
 * 在java.io包中的操作主要有字节流和字符流两大类，两类都有输入输出操作。
 * 在字节流中输出数据主要是OutputStream类完成，输入用的是InputStream类，
 * 在字符流中输出主要是使用Writer类完成，输入主要是使用Reader类完成。
 * 在Java中IO操作也是有步骤的，以文件的操作为例，主要操作流程有：
 * ①使用File类打开一个文件
 * ②通过字节流或者字符流的子类指定输出的位置
 * ③进行读/写操作
 * ④关闭输入/输出
 * <p>
 * 字节流
 * 字节流主要是操作byte类型数据，以为byte数组为准，主要操作类就是OutputStream类和InputStream类
 * 1.字节输出流：OutputStream
 * OutputStream是整个IO包中字节输出流的最大父类 而且这个类是一个抽象类，如果想要使用必须要通过子类实例化对象。
 * 如果现在想要操作是一个文件，可以使用FileOutputStream类，通过向上转型后，可以为OutputStream类思力华。
 * 在Output Stream类中的主要操作方法：
 * close()：关闭输出流
 * flush()：刷新缓冲区
 * write(byte[] b)：将一个byte数组写入数据流
 * write(byte[] b,int off,int len)：将一个指定范围的byte数组写入数据流
 * write(int b)：将一个字节数据写入数据流
 * 此时可使用FileOutputStrem类的子类，此类的构造方法如下：
 * public FileOutputStream(File file) throws FileNotFoundException
 * 操作时候必须接收File类的实例，指明要输出的文件路径
 * 提示：关于Output Stream类的组成说明：
 * 在类定义中，在JDk1.5之后OutputStream类同时实现AutoCloseable接口和Flushable两个父接口。
 * 通过父接口可以发现close()和flush()都已经在OutputStream中明确定义过了，这是因为在JDK1.0的时候并没有为Output Stream类设计接口，
 * 而从JDK1.5之后考虑到标准的做法，才增加了两个父接口，不过由于最初的习惯 这两个接口很少被关注。
 * 但是JDK1.7之后对于接口的定义又改变了，Closeable接口继承了AutoCloseable父接口。
 * 为什么又要提供这个AutoCloable接口呢？
 * 主要是因为开发者总是忘记手工关闭资源。但是目前大多数开发者还是选择手工关闭资源。
 * <p>
 * 3.字节输入流：InputStream
 * 可以通过InputStream类从文件中把内容读取进来。
 * InputStream本身也是一个抽象类，必须依靠其子类，如果现在是从文件中读取，子类肯定是FileInputStream.
 * InputStream类中的主要方法有：
 * available()：可以取得输入文件的大小
 * close()：关闭输入流
 * read()：读取内容，以数字的方式读取
 * read(byte[] b)：将内容读取到字节数组中，同时返回读入的个数
 * FileInputStream类的构造方法：
 * public FileInputStream(File file) throws FileNotFound
 * <p>
 * 字符流
 * 在程序中一个字符等于两个字节 Java提供了Reader和Writer两个专门的操作字符流的类
 * 1.字符输出流 Writer
 * 此类本身也是一个抽象类，想要使用也要使用其子类，如果此时是向文件中写入内容，应该使用FileWriter的子类
 * Writer类常用方法：
 * close()：关闭输出流
 * writer(String str)：将字符串输出
 * writer(char[] cbuf)：将字符数组输出
 * flush()：强制性清空缓存
 * FileWriter类的构造方法定义：
 * public FileWriter(File file) throws IOException
 * <p>
 * 提示：关于Appendable接口的说明
 * 在Writer类中除了实现Closeable和Flushable两个接口之外 还实现了Appendable接口。
 * 此接口表示内容可以被追加，接收的参数是CharSequence,实际上String类就实现了此接口
 * 所以可以直接通过此接口的方法向输出流中追加内容
 * <p>
 * 3.字符输入流：Reader
 * Reader类本身也是抽象类，如果现在要从文件中读取内容，则可以直接使用FileReader子类
 * Reader类的常用方法有：
 * close()：关闭输出流
 * read()：读取单个字符
 * read(char[] cbuf)：将内容读取到字符数组之中，返回读入的长度
 * <p>
 * FileReader类的构造方法：
 * public FileReader(File file) throws FileNotFoundException
 * <p>
 * 字节流和字符流的区别
 * 字节流在操作的时候本身不会用到缓冲区（内存）
 * 是于文件本身直接操作的，而字符流在操作的时候使用缓冲区，通过缓冲区再操作文件。
 * 字节流输出：程序 --> 字节流（直接操作文件） --> 文件
 * 字节流输出：程序 --> 字节流 --> 缓存（数据先放在缓存之后，再从缓存写入文件）  -->  文件
 * <p>
 * 转换流--OutputStreamWriter类和InputStreamReader类
 * 在整个IO包中，实际上就是分为字节流和字符流 ，但是除了这两个流之外 还有一组字节流--字符流的转换类。
 * OutputStreamWriter类是Writer类的子类
 * InputStreamReader类是Reader类的子类
 * 转换流可以将字节流转成字符流，同时指定读取文件时按照怎样的编码方式。
 * 若在txt文件中出现中文，并且该文件没有使用UTF-8形式编码；在使用IO流读取文件时就会出现乱码问题。
 * 转换流可以解决乱码问题，转换流可以将字节流转成字符流，同时指定读取文件时按照怎样的编码方式。
 * 常见转换流有两个 —— InputStreamReader 和 OutputStreamWriter。
 * 提示：
 * FileOutputStream是OutputStream类的直接子类，FileInputStream是InputStream类的直接子类，
 * 但是在字符流文件的两个操作类却有一些特殊，
 * FileWriter并不是Writer类的子类，而是OutputStreamWriter的子类
 * 而FileReader也不是Reader类的子类，而是InputStreamReader的子类
 * 中间都需要转换操作？（我有疑惑 在转换流这个，感觉讲的不是太好）
 * 从继承关系来看 不管是字节流还是字符流 最后都是以字节的形式输入\输出的
 * <p>
 * 内存操作流
 * 假设现在某一种应用需要进行IO操作，但是不想在磁盘上产生文件，就可以把内存当作一个临时文件进行操作。
 * Java中对内存操作提供的有两类：
 * ①字节内存流：ByteArrayInputStream（内存字节输入流）、ByteArrayOutputStream（内存字节输出流）
 * ②字符内存流：CharArrayReader（内存字符输入流）、CharArrayWriter（内存字符输出流）
 * ByteArrayInputStream主要方法：
 * ByteArrayInputStream(byte[] buf)：将全部内容写入内存中
 * ByteArrayInputStream(byte[] buf,int offset,int length)：将指定范围的内容去全部写到内存之中
 * <p>
 * ByteArrayOutputStream主要方法：
 * ByteArrayOutputStream():构造方法，创建对象
 * write(int b)：将内容从内存中输出
 * <p>
 * 管道流
 * 主要作用就是进行两个线程之间的通信。
 * 分为管道输出流(PipedOutputStream)
 * 管道输入流(PipedInputStream)
 * 如果想要进行管道输出，则必须把输出流连在输入流上
 * 在PipedOutputStrem类上有如下方法连接管道：
 * public void connect(PipedInputStream snk) throws IOException
 * <p>
 * 打印流
 * 利用OutputStream类可以实现数据的输出操作，但是OutputStream类本身只支持字节数据的输出
 * 为了提高输出操作的编写效率，往往会利用打印流来实现输出数据的功能。
 * 整个IO包中，打印流是输出信息最方便的类
 * 包含字节打印流(PrintStream)和字符打印流(PrintWriter)
 * PrintStream是OutputStream的子类
 * 常用方法：
 * PrintStream(File file)：通过一个File对象实例化PrintStream类
 * PrintStream(OutputStream out)：接收OutputStream对象，实例化PrintStream类
 * PrintStream printf(Local l,String format,Object...args)：根据指定的Locale进行格式化输出
 * PrintStream printf(String format,Object...args)：根据本地环境格式化输出
 * print(boolean b)：输出任意数据
 * println(boolean b)：输出任意数据后换行
 * 在上面的构造方法中可以发现，有一个构造方法可以直接接收OutputStream类的实例
 * 这是因为跟OutputStream类相比，PrintStream可以更加方便的输出数据，好比将OutputStream类重新包装了一下，使之输出更加方便。
 * 其实就是把输出流实例传递到打印流中之后，把输出流重新装饰了一下，就像送别人礼物，需要把礼物包装一下，
 * 这样的设计模式叫做装饰设计模式。
 * <p>
 * System类对IO的支持
 * System类对IO给予了一定的这次hi，在System类中定义了3个常量：
 * public static final PrintStream out：对应系统标准输出，一般是显示器
 * public static final PrintStream err：错误信息输出
 * public static final InputStream in：对应着标准输入，一般是键盘
 * 提示：这三个常量也不符合命名规则，也是Java历史的产物
 * <p>
 * 通过System类还可以改变System.in的输入流来源以及System.out System.err的两个输出流的输出位置
 * 操作方法:
 * setOut(PrintStream out)：重定向”标准“输出瑞
 * setErr(PrintStream err)：重定向”标准“错误输出流
 * setIn(InputStream in)：重定向”标准“输入流
 * <p>
 * BufferedReader类
 * BufferedReader类是从缓存区之中读取内容
 * 所有的输入字节数据都将放在缓冲区
 * 常用方法：
 * public BufferedReader(Reader in)：接收一个Reader类的实例
 * public String readLine() throws IOException：一次性从缓冲区中将内容全部读取进来
 * BufferedReader中定义的构造方法只能接收 “字符输入流”的实例 ，所以必须使用字符输入流-字节输出流的转换了InputStreamReader，将字节
 * 输入流System.in变为字符流
 * 提示：代码的说明
 * BufferedReader类只能接收缓冲区的字符流，因为每一个中文要占两个字节，所以要将System.in这个字节的输入流变为字符的输入流
 * <p>
 * Scanner类
 * 在JDK1.5之前提供了BufferedReader类方便用户进行输入流数据的读取操作，而从JDK1.5开始为了简化输入流的操作又提供了Scanner类
 * 这个类不仅仅可以完成之前的输入数据操作，也可以方便的对输入数据进行验证。
 * 这个类在java.util包中。
 * 常用方法：
 * Scanner(File source)：从文件中接收内容
 * Scanner(InputStream source)：从指定的字节输入流中接收内容
 * boolean hasNext(Pattern pattern)：判断输入的数据是否符合指定的正则标准
 * boolean hasNextInt()：判断输入的是否是整数
 * boolean hasNextFloat()：判断输入的是否是小数
 * String next()：接收内容
 * String next(Pattern pattern)：接收内容，进行正则验证
 * int nextInt()：接收数字
 * float nextFloat()：接收小数
 * Scanner useDelimiter(String pattern)：设置读取的分隔符
 * 提示：Scanner类可以接收任意的输入流
 * 因为Scanner类中有一个可以接收InputStream类的构造方法，表示只要是直接输入流的子类，就都可以通过Scanner类案例化，以便读取
 * <p>
 * 数据操作流
 * 在IO包中，提供了两个与平台无关的数据操作流，分别是
 * 数据输出流：DataOutputStream
 * 数据输入流：DataInputStream
 * 通常数据输出流会按照一定的格式将数据输出，再通过数据输入流按照一定的格式将数据读入，这样可以方便的对数据进行处理
 * <p>
 * DataOutputStream类
 * 是OutputStream类的子类，此类定义：
 * public class DataOutputStream extend FilterOutputStream implements DataOutput
 * 提示：DataOutput接口的作用
 * 是数据的输出接口，其中定义了各种数据的输出操作方法
 * 比如：在DataOutputStream类中的各种writeXXX方法就是此接口定义的，但是在数据输出时一般都会直接使用DataOutputStream
 * 只有在对象序列化的时候才有可能直接操作到此接口。这一点会在Externalizable接口时为读者介绍
 * DataOutputStream常用方法：
 * public DataOutputStream(OutputStream out)
 * public final void writeInt(int v)
 * public final void writeDouble(double v)
 * public final void writeChars(String s)
 * public final void writeChar(int v)
 * <p>
 * DataInputStream是InputStream类的子类，专门负责读取使用DataOutputStream输出的数据
 * 此类的定义：public class DataInputStream extends FilterInputStream implements DataInput
 * 此类继承子FilterInputStream(FileterInputStream 是InputStream类的子类),同时实现DataInput接口
 * 在DataInput接口中定义了一系列读入数据的方法
 * 提示：DataInput接口的作用
 * 这个接口是读取数据的操作接口，与DataOutput接口提供的各种writeXXX方法对应，在此接口中定义了一些列的readXXX方法，
 * 一般操作的时候不会直接利用到此接口，而主要使用DataInputSream类完成爬取功能，只有在对象序列化订单时候才有可能直接利用此接口读取数据
 * 这一点在讲解Externalizable接口的时候在介绍。
 * DataInputStream类常用方法：
 * DataInputStream(InputStream in)：构造方法，实例化对象
 * int readInt()：从输入流中读取整数
 * fload readFloat()：从输入流中读取小数
 * char readChar()：从输入流中读取一个字符
 * <p>
 * 合并流
 * 合并流的功能主要是将两个文件的内容合并成一个文件
 * 要实现合并流 必须使用SequenceInputStream类，这个类的常用方法有：
 * public SequenceInputStream(InputStream s1,InputStream s2)：使用两个输入流实例化本类对象
 * public int available() throws IOException：返回文件大小
 * <p>
 * 压缩流
 * 在Java中，可以把文件压缩成ZIP,JAR,GZIP等文件形式
 * <p>
 * ZIP压缩输入/输出流简介
 * 要实现ZIP的压缩，要导入java.util.zip包，可以使用包中的ZipFile，ZipOutputStream，ZipInputStream,ZipEntry几个类完成操作
 * 提示：JAR及GZIP文件格式的压缩输入 输出流
 * Jar压缩的支持类保存在java.util.jar包中，常用类有：
 * ①Jar压缩输出流：JarOutputStream
 * ②Jar压缩输入流：JarInputStream
 * ③Jar文件：JarFile
 * ④Jar实体：JarEntry
 * GZIP是用于UNIX系统总的文件压缩，在Linux中经常会使用*.gz的文件，就是GZIP格式，GZIP压缩的支持类保存在java.util.zip包中，常用类有：
 * ①GZIP压缩输出流：GZIPOutputStream
 * ②GZIP压缩输入流：GZIPInputStream
 * 在每一个压缩文件中都存在多个子文件，每一个子文件在Java中就用ZipEntry表示。】
 * ZipEntry类的常用方法：
 * ZipEntry(String name)：创建对象并指定要创建的ZipEntry名称
 * boolean isDirectory()：判断此ZipEntry是否是目录
 * <p>
 * ZipFile类
 * Java中 每一个压缩文件都可以用ZipFile表示，还可以根据ZipFile根据压缩后的文件名称找到每一个压缩文件的ZipEntry并且将其解压缩。
 * 常用方法：
 * ZipFile(File file)：构造方法 根据File类实例化Zipfile对象
 * ZipEntry getEntry(String name)：根据名称找到其对应的ZipEntry
 * InputStream getInputStream(ZipEntry entry)：根据ZipEntry取得InputStream实例
 * getName():得到压缩文件的路径名称
 * <p>
 * ZipInputStream类是InputStream类的子类，通过此类，可以方便的读取ZIP格式的压缩文件
 * 常用方法：
 * ZipInputStream(InputStream in):实例化ZipInputStream对象
 * ZipEntry getEntry()：取得下一个ZipEntry
 * <p>
 * 在JavaIO中 所有的数据都是采用顺序的读取方式，对于一个输入流来说，都是采用从头到尾的顺序读取的。
 * 如果输入流中某个不需要的内容被读取叫你来，则只能通过程序将这些不需要的内容处理掉。
 * 为了解决这样的问题，Java提供了一种回退输入流（PushbackInputStream,PushbackReader)，
 * 可以把读取进来的某些数据重新退回到输入流的缓冲区中。
 * 常用方法：
 * PushbackInputStream(InputStream in)：构造 将输入流放入回退流中
 * int read():读取数据
 * int read(byte[] b,int len)：读取指定范围的数据
 * unread(int b)：回退一个数据到缓冲区前面
 * unread(bte[] b)：回退一组数据到缓冲区前面
 * unread(byte[] b,int off,int len):回退指定范围的一组数据到缓冲区前面
 * <p>
 * 回退流与输入流的对应：
 * 读取一个：read()                              回退一个：unread(int b)
 * 读取一组：read(byte[] b)                      回退一组:unread(byte[] b)
 * 读取部分：read(byte[] b,int off,int len)      回退部分：unread(bye[] b,int off,int len)
 * <p>
 * <p>
 * 字符编码：
 * 计算机的世界里面，任何的文字都是以指定的编码方式存在的，在Java中常见的是ISO8859-1,GBK/GB2312,unicode,UTF编码
 * ISO8859-1属于单字节编码，最多只能表示0~255的字符范围，主要用在英文上
 * GBK/GB2312：中文的国标码，专门用来表示汉字，是双字节编码，如果再次编码中出现了中文则使用ISO8859-1编码，GBK可以表示简体中文和繁体中文
 * 而GB2312只能表示简体中文，GBK是兼容GB2312的。
 * Unicode:Java中用的就是此编码方式，也是最标准的一种编码，是十六进制表示的编码，但是此编码不兼容ISO8859-1编码。
 * UTF:由于Unicode不支持ISO8859-1编码，而且容易占用更多的空间，对于英文字母也需要两个字节编码，这样使用unicode不便于传输和存储，
 * 因此产生了UTF编码，UTF编码兼容了ISO8859-1编码，同时也用来表示所有的怨言字符，不过UTF编码是不定长编码，每一个字符的长度为1-6个字节不等，
 * 一般在中文网页中使用此编码，因为这样可以节省空间。
 * 如果想要知道本地的默认编码，就用System类完成字符编码
 * <p>
 * 对象序列化
 * 序列化是为了二进制数据的传输。
 * 对象序列化是把一个对象变为二进制的数据流的方法，通过对象序列化可以方便低实现对象的传输和存储
 * 如果一个类的对象想要被序列化，则对象所在的类必须实现java.io.Serializable{}接口
 * public interface Serializable{}
 * 可以发现这个接口没有定义任何犯法，所以此接口是一个标识接口。表示一个类具备了被序列化的能力
 * ---------------序列化------------------------------反序列化------------->
 * 程序 ---ObjectOutputStream-->  序列化对象  ---ObjectInputStream--->   程序
 * <p>
 * 提示：对象序列化和对象反序列化操作的版本兼容性问题
 * 在对象序列化或者反序列化操作的时候，要考虑JDK版本的问题，
 * 如果序列化的JDK版本和反序列化的版本不一致 可能会造成异常。
 * 所以在序列化操作中引入了一个serialVersionUID的常量，
 * 可以通过此常量来验证版本的一致性。在进行反序列化时，JVM胡
 * 把传来的字节流中的serialVersionUID与本地相应实体（类）的serialVersionUID进行比较，
 * 如果相同就认为是一致的，可以反序列化，否则就会出现版本不一致的异常。
 * <p>
 * 当实现java.ioSerializable接口的实体（类）没有显式的定义一个名为serialVersionUID,类型为long的变量时，
 * Java序列化机制在编译时会自动生成一个此版本的serialVersionUID。
 * 当然，如果不希望通过编译来自动生成，也可以直接显示地定义一个名为serialVersionUID，类型为long的变量，
 * 只要不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。
 * <p>
 * 对象输出流：ObjectOutputStream
 * 一个对象如果想要进行输出，则必须使用ObjectOutputStream类，此类定义如下：
 * public class ObjectOutputStream extends OutputStream implements ObjectOoutput,ObjectStreamContains
 * ObjectOutputStream类时OutputStream类的子类
 * 常用方法：
 * ObjectOutputStream(OutputStream out)：构造方法，传入输入的对象
 * public final void writeObject(Object obj)：输出对象
 * 这个类的使用形式和PrintStream非常相似，在实例化的时候也需要传入一个OutputStream的子类对象
 * 然后根据传入的OutputStream子类的对象不同，输出的位置也不同
 * <p>
 * 对象输入流ObjectInputStream
 * 这个流是InputStream流的子类，与PrintStream类使用类似，此类同样需要接收InputStream类的实例才可以实例化。
 * 操作方法：
 * ObjectInputStream(InputStream in)：构造输入对象
 * final Object readObject()：从指定位置读取对象
 * 用对象输入流将刚刚保存在ObjecctOutputStream.txt中的内容读取出来，这个过程有时候也叫反序列化
 * <p>
 * 说明：可不可以让所有的类都实现接口
 * 既然这个接口中没有任何的方法，也不会让实现这个接口的类增加不必要的操作，那么所有的类都实现这个接口是不是更好。
 * 回答：不可以，这样在日后的版本升级中会存在问题
 * 如果在以后的JDK版本中修改了这个Serailizable接口而且又增加了许多方法，则以往所有系统中的类都要修改了，这样肯定会麻烦。
 * 所以只用在需要序列化对象的类实现Serializable接口。
 * <p>
 * Externalizable接口
 * 有需要的选择序列化属性，指定序列化的内容
 * 这个接口是Serializable接口的子接口，在接口中定义了两个方法，两方法作用如下：
 * ①writeExternal(ObjectOutput out)：方法中指定了要保存的属性信息，对象序列化时调用
 * ②readExternal(ObjectInput in):方法中读取被保存的信息，对象反序列化的时候调用。
 * 这两个参数的类型是：ObjectOuput 和ObjectInput ，两接口的定义：
 * ①public interface ObjectOutput extends DataOutput
 * ②public interface ObjectInput extends DataInput
 * 可以发现这两接口都分别继承了DataOutput DataInput，这样在这两个方法中就可以像DataOuputStream和DataInputStream那样
 * 直接输出和读取各种类型的数据
 * 如果一个类使用Externalizable实现序列化的时候，在此类中必须存在一个无参构造方法，
 * 因为在反序列化时候会默认调用无参构造实例化对象，如果没有此无参构造，则运行的时候会出现异常
 * 这一点的实现机制和Serialiable接口是不同的
 * <p>
 * Serializable                Externalizable
 * 实现复杂度： 实现简单，Java对其有内建支持      实现复杂，由开发人员自己完成
 * 执行效率 ：  所有对象由Java统一保存，性能低    开发人员决定哪个对象保存，可能的速度提升
 * 保存信息 ： 保存时占用的空间大                部分存储，可能的空间减少
 * <p>
 * 在一般的开发中，因为Serializable接口的使用更方便。
 *
 * transient关键字
 * Serializable接口实现的操作实际上是将一个对象中的全部属性进行序列化，当然也可以用Externalizable接口实现部分属性的序列化
 * 但是这样操作麻烦
 * 当使用Serializable接口实现序列化操作的时候，如果一个对象的属性不希望被序列化的时候
 * 可以使用transient关键字声明。
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */